chore: effect rpc and layer based startup#929
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Autofix Details
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Dev URL redirect drops request path and query
- Fixed the redirect to construct a new URL from the request's pathname and search params against the dev origin, and updated the test to assert the correct behavior.
Or push these changes by commenting:
@cursor push 089fb595df
Preview (089fb595df)
diff --git a/apps/server/src/http.ts b/apps/server/src/http.ts
--- a/apps/server/src/http.ts
+++ b/apps/server/src/http.ts
@@ -90,7 +90,8 @@
const config = yield* ServerConfig;
if (config.devUrl) {
- return HttpServerResponse.redirect(config.devUrl.href, { status: 302 });
+ const devTarget = new URL(`${url.pathname}${url.search}`, config.devUrl);
+ return HttpServerResponse.redirect(devTarget.href, { status: 302 });
}
if (!config.staticDir) {
diff --git a/apps/server/src/server.test.ts b/apps/server/src/server.test.ts
--- a/apps/server/src/server.test.ts
+++ b/apps/server/src/server.test.ts
@@ -266,7 +266,7 @@
const response = yield* Effect.promise(() => fetch(url, { redirect: "manual" }));
assert.equal(response.status, 302);
- assert.equal(response.headers.get("location"), "http://127.0.0.1:5173/");
+ assert.equal(response.headers.get("location"), "http://127.0.0.1:5173/foo/bar");
}).pipe(Effect.provide(NodeHttpServer.layerTest)),
);|
|
||
| const config = yield* ServerConfig; | ||
| if (config.devUrl) { | ||
| return HttpServerResponse.redirect(config.devUrl.href, { status: 302 }); |
There was a problem hiding this comment.
Dev URL redirect drops request path and query
High Severity
The dev URL redirect uses config.devUrl.href without appending the incoming request's pathname or search params. Every GET request (e.g. /settings?tab=general) redirects to the dev server root (http://127.0.0.1:5173/) instead of the matching route (http://127.0.0.1:5173/settings?tab=general). The reference implementation correctly constructs new URL(url.pathname + url.search, origin). The test at line 269 asserts the broken behavior, masking the regression.
Additional Locations (1)
ede8523 to
8ea82bc
Compare
- move project favicon resolution into the Effect HTTP router - create derived runtime directories during config resolution - update desktop/server entrypoints and web URL helpers
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 4 total unresolved issues (including 3 from previous reviews).
Autofix Details
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Git error message loses operation/command/cwd context
- Restored the rich formatted message getters in all four error classes (GitCommandError, GitHubCliError, TextGenerationError, GitManagerError) in packages/contracts/src/git.ts to include operation, command, and cwd context, matching the original local definitions.
Or push these changes by commenting:
@cursor push 5eba9380c8
Preview (5eba9380c8)
diff --git a/packages/contracts/src/git.ts b/packages/contracts/src/git.ts
--- a/packages/contracts/src/git.ts
+++ b/packages/contracts/src/git.ts
@@ -232,7 +232,7 @@
cause: Schema.optional(Schema.Defect),
}) {
override get message(): string {
- return this.detail;
+ return `Git command failed in ${this.operation}: ${this.command} (${this.cwd}) - ${this.detail}`;
}
}
@@ -242,7 +242,7 @@
cause: Schema.optional(Schema.Defect),
}) {
override get message(): string {
- return this.detail;
+ return `GitHub CLI failed in ${this.operation}: ${this.detail}`;
}
}
@@ -255,7 +255,7 @@
},
) {
override get message(): string {
- return this.detail;
+ return `Text generation failed in ${this.operation}: ${this.detail}`;
}
}
@@ -265,7 +265,7 @@
cause: Schema.optional(Schema.Defect),
}) {
override get message(): string {
- return this.detail;
+ return `Git manager failed in ${this.operation}: ${this.detail}`;
}
}You can send follow-ups to this agent here.
The message getters for GitCommandError, GitHubCliError, TextGenerationError, and GitManagerError in contracts were returning only this.detail, losing the operation, command, and cwd context that the original local definitions included. Restore the formatted messages so that logging and error reporting surfaces full debugging context. Applied via @cursor push command
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 4 total unresolved issues (including 3 from previous reviews).
Autofix Details
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Stricter bootstrap schema silently breaks graceful fallback
- Changed BootstrapEnvelopeSchema mode field back to Schema.optional(Schema.String) and added an isRuntimeMode filter at resolution time so unrecognized mode values are silently ignored and fall back to "web".
Or push these changes by commenting:
@cursor push 4405ddf176
Preview (4405ddf176)
diff --git a/apps/server/src/cli.ts b/apps/server/src/cli.ts
--- a/apps/server/src/cli.ts
+++ b/apps/server/src/cli.ts
@@ -17,8 +17,10 @@
const PortSchema = Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 65535 }));
+const isRuntimeMode = Schema.is(RuntimeMode);
+
const BootstrapEnvelopeSchema = Schema.Struct({
- mode: Schema.optional(RuntimeMode),
+ mode: Schema.optional(Schema.String),
port: Schema.optional(PortSchema),
host: Schema.optional(Schema.String),
t3Home: Schema.optional(Schema.String),
@@ -148,7 +150,9 @@
resolveOptionPrecedence(
flags.mode,
Option.fromUndefinedOr(env.mode),
- Option.flatMap(bootstrapEnvelope, (bootstrap) => Option.fromUndefinedOr(bootstrap.mode)),
+ Option.flatMap(bootstrapEnvelope, (bootstrap) =>
+ Option.filter(Option.fromUndefinedOr(bootstrap.mode), isRuntimeMode),
+ ),
),
() => "web",
);You can send follow-ups to this agent here.
Co-authored-by: codex <codex@users.noreply.github.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 5 total unresolved issues (including 4 from previous reviews).
Autofix Details
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Background git refresh loses all services with
Effect.runFork- Restored
Effect.forkScopedin place ofEffect.sync(() => { Effect.runFork(...) })which was incorrectly introduced during a merge, so the background upstream refresh now inherits all service layers from the enclosing scope.
- Restored
Or push these changes by commenting:
@cursor push 460b46b80b
Preview (460b46b80b)
diff --git a/apps/server/src/git/Layers/GitCore.ts b/apps/server/src/git/Layers/GitCore.ts
--- a/apps/server/src/git/Layers/GitCore.ts
+++ b/apps/server/src/git/Layers/GitCore.ts
@@ -1941,11 +1941,9 @@
});
// Refresh upstream refs in the background so checkout remains responsive.
- yield* Effect.sync(() => {
- Effect.runFork(
- refreshCheckedOutBranchUpstream(input.cwd).pipe(Effect.ignoreCause({ log: true })),
- );
- });
+ yield* Effect.forkScoped(
+ refreshCheckedOutBranchUpstream(input.cwd).pipe(Effect.ignoreCause({ log: true })),
+ );
},
);You can send follow-ups to this agent here.
- Route workspace lookups through shared path normalization - Inline the project favicon fallback and remove the health route - Update tests for normalized workspace errors and favicon serving
- Return progress events inline from git.runStackedAction - Remove the separate git action progress subscription path - Update web/native clients and tests to consume streamed results
- Remove trailing commas from `apps/web/tsconfig.json` - Keep TypeScript config consistent with project formatting rules
- Move WebSocket RPC schemas into `contracts/rpc` - Rename welcome payload to `ServerLifecycleWelcomePayload` - Update server attachment serving to use file responses
- Extract shared websocket RPC protocol layer - Add atom-backed RPC client and registry provider - Update transport and tests for the new client path
- Centralize server config and welcome state in `rpc/serverState` - Bootstrap server snapshots from the RPC client in one place - Update web consumers and tests for the new server state flow
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
There are 3 total unresolved issues (including 1 from previous review).
Bugbot Autofix prepared fixes for both issues found in the latest run.
- ✅ Fixed: Planning doc committed with developer-local absolute paths
- Replaced all 25 occurrences of the developer-local absolute path prefix
/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/with empty string, making all markdown links use repository-relative paths.
- Replaced all 25 occurrences of the developer-local absolute path prefix
- ✅ Fixed: Detached fork leaks fiber beyond scope lifetime
- Restored
Effect.forkScoped(replacingEffect.forkDetach({ startImmediately: true })) so the background upstream refresh fiber is tied to the enclosing scope and properly interrupted during shutdown, consistent with the type signature requiringScope.Scope.
- Restored
Or push these changes by commenting:
@cursor push bd40a77074
Preview (bd40a77074)
diff --git a/.plans/effect-atom.md b/.plans/effect-atom.md
--- a/.plans/effect-atom.md
+++ b/.plans/effect-atom.md
@@ -1,20 +1,20 @@
# Replace React Query With AtomRpc + Atom State
## Summary
-- Use `effect/unstable/reactivity/AtomRpc` over the existing `WsRpcGroup`; stop wrapping RPC in promises via [wsRpcClient.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/wsRpcClient.ts) and [wsNativeApi.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/wsNativeApi.ts).
+- Use `effect/unstable/reactivity/AtomRpc` over the existing `WsRpcGroup`; stop wrapping RPC in promises via [wsRpcClient.ts](apps/web/src/wsRpcClient.ts) and [wsNativeApi.ts](apps/web/src/wsNativeApi.ts).
- Keep Zustand for orchestration read model and UI state.
- Keep a narrow `desktopBridge` adapter for dialogs, menus, external links, theme, and updater APIs.
- Do not introduce Suspense in this migration. Atom-backed hooks should keep returning `data`, `error`, `isLoading|isPending`, `refresh`, and `mutateAsync`-style surfaces so component churn stays low.
## Target Architecture
-- Extract the websocket `RpcClient.Protocol` layer from [wsTransport.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/wsTransport.ts) into `rpc/protocol.ts`.
+- Extract the websocket `RpcClient.Protocol` layer from [wsTransport.ts](apps/web/src/wsTransport.ts) into `rpc/protocol.ts`.
- Define one `AtomRpc.Service` for `WsRpcGroup` in `rpc/client.ts`.
- Add `rpc/invalidation.ts` with explicit scoped invalidation keys: `git:${cwd}`, `project:${cwd}`, `checkpoint:${threadId}`, `server-config`.
- Add `platform/desktopBridge.ts` as the only browser/desktop facade.
-- Remove from web by the end: [wsNativeApi.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/wsNativeApi.ts), [nativeApi.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/nativeApi.ts), [wsNativeApiState.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/wsNativeApiState.ts), [wsNativeApiAtoms.tsx](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/wsNativeApiAtoms.tsx), [wsRpcClient.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/wsRpcClient.ts), and all `*ReactQuery.ts` modules.
+- Remove from web by the end: [wsNativeApi.ts](apps/web/src/wsNativeApi.ts), [nativeApi.ts](apps/web/src/nativeApi.ts), [wsNativeApiState.ts](apps/web/src/wsNativeApiState.ts), [wsNativeApiAtoms.tsx](apps/web/src/wsNativeApiAtoms.tsx), [wsRpcClient.ts](apps/web/src/wsRpcClient.ts), and all `*ReactQuery.ts` modules.
## Phase 1: Infrastructure First
-1. Extract the shared websocket RPC protocol layer from [wsTransport.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/wsTransport.ts) without changing behavior.
+1. Extract the shared websocket RPC protocol layer from [wsTransport.ts](apps/web/src/wsTransport.ts) without changing behavior.
2. Build the AtomRpc client on top of that layer.
3. Add one temporary `runRpc` helper for imperative handlers that still want `Promise` ergonomics; it must call the AtomRpc service directly and must not reintroduce a facade object.
4. Replace manual registry wiring with one app-level registry provider based on `@effect/atom-react`.
@@ -22,21 +22,21 @@
## Phase 2: Replace `wsNativeApi`-Owned Push State
1. Migrate welcome/config/provider/settings state first, because it is already atom-shaped and is the lowest-risk way to delete `wsNativeApi` responsibilities.
-2. Replace [wsNativeApiState.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/wsNativeApiState.ts) with `rpc/serverState.ts`, updated directly from `subscribeServerLifecycle` and `subscribeServerConfig`.
+2. Replace [wsNativeApiState.ts](apps/web/src/wsNativeApiState.ts) with `rpc/serverState.ts`, updated directly from `subscribeServerLifecycle` and `subscribeServerConfig`.
3. Keep the current hook names for one PR: `useServerConfig`, `useServerSettings`, `useServerProviders`, `useServerKeybindings`, `useServerWelcomeSubscription`, `useServerConfigUpdatedSubscription`.
-4. Move bootstrap side effects out of [wsNativeApiAtoms.tsx](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/wsNativeApiAtoms.tsx) into a new root bootstrap component mounted from [__root.tsx](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/routes/__root.tsx).
-5. Delete the `server.getConfig()` fallback logic from [wsNativeApi.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/wsNativeApi.ts); snapshot fetch now lives beside the stream atoms.
+4. Move bootstrap side effects out of [wsNativeApiAtoms.tsx](apps/web/src/wsNativeApiAtoms.tsx) into a new root bootstrap component mounted from [__root.tsx](apps/web/src/routes/__root.tsx).
+5. Delete the `server.getConfig()` fallback logic from [wsNativeApi.ts](apps/web/src/wsNativeApi.ts); snapshot fetch now lives beside the stream atoms.
## Phase 3: Replace React Query Domain By Domain
-1. Replace [gitReactQuery.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/lib/gitReactQuery.ts) first.
+1. Replace [gitReactQuery.ts](apps/web/src/lib/gitReactQuery.ts) first.
2. Add `rpc/gitAtoms.ts` and `rpc/useGit.ts` with `useGitStatus`, `useGitBranches`, `useResolvePullRequest`, and `useGitMutation`.
3. Mutation settlement must invalidate scoped keys, not a global cache. `checkout`, `pull`, `init`, `createWorktree`, `removeWorktree`, `preparePullRequestThread`, and stacked actions invalidate `git:${cwd}`. Worktree create/remove also invalidates `project:${cwd}`.
-4. Replace [projectReactQuery.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/lib/projectReactQuery.ts) second. `useProjectSearchEntries` must preserve current “keep previous results while loading” behavior.
-5. Replace [providerReactQuery.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/lib/providerReactQuery.ts) third. Preserve current checkpoint error normalization and retry/backoff semantics inside the atom effect. Invalidate by `checkpoint:${threadId}`.
+4. Replace [projectReactQuery.ts](apps/web/src/lib/projectReactQuery.ts) second. `useProjectSearchEntries` must preserve current “keep previous results while loading” behavior.
+5. Replace [providerReactQuery.ts](apps/web/src/lib/providerReactQuery.ts) third. Preserve current checkpoint error normalization and retry/backoff semantics inside the atom effect. Invalidate by `checkpoint:${threadId}`.
6. Defer the desktop updater until the last phase.
## Phase 4: Move Root Invalidation Off `queryClient`
-1. In [__root.tsx](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/routes/__root.tsx), remove `QueryClient` usage and replace the throttled `invalidateQueries` block with throttled invalidation helpers.
+1. In [__root.tsx](apps/web/src/routes/__root.tsx), remove `QueryClient` usage and replace the throttled `invalidateQueries` block with throttled invalidation helpers.
2. Keep Zustand orchestration/event application unchanged.
3. Map current effects exactly:
- git or checkpoint-affecting orchestration events touch `checkpoint:${threadId}`
@@ -50,14 +50,14 @@
- `rpc/gitActions.ts`
- `rpc/projectActions.ts`
- `platform/desktopBridge.ts`
-2. Migrate direct [nativeApi.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/nativeApi.ts) callers by domain, not file-by-file: git-heavy components first, then orchestration/thread actions, then shell/dialog helpers.
-3. After the last caller is gone, delete [nativeApi.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/nativeApi.ts) and the `window.nativeApi` fallback entirely.
-4. In the final cleanup PR, remove `NativeApi` from [ipc.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/packages/contracts/src/ipc.ts) if nothing outside web still needs it.
+2. Migrate direct [nativeApi.ts](apps/web/src/nativeApi.ts) callers by domain, not file-by-file: git-heavy components first, then orchestration/thread actions, then shell/dialog helpers.
+3. After the last caller is gone, delete [nativeApi.ts](apps/web/src/nativeApi.ts) and the `window.nativeApi` fallback entirely.
+4. In the final cleanup PR, remove `NativeApi` from [ipc.ts](packages/contracts/src/ipc.ts) if nothing outside web still needs it.
## Phase 6: Remove React Query Completely
1. Delete `@tanstack/react-query` from `apps/web/package.json`.
-2. Remove `QueryClientProvider` and router context from [router.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/router.ts) and [__root.tsx](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/routes/__root.tsx).
-3. Replace [desktopUpdateReactQuery.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/lib/desktopUpdateReactQuery.ts) with a writable atom plus `desktopBridge.onUpdateState`.
+2. Remove `QueryClientProvider` and router context from [router.ts](apps/web/src/router.ts) and [__root.tsx](apps/web/src/routes/__root.tsx).
+3. Replace [desktopUpdateReactQuery.ts](apps/web/src/lib/desktopUpdateReactQuery.ts) with a writable atom plus `desktopBridge.onUpdateState`.
4. Delete the old query-option tests.
## Public Interfaces And Types
@@ -70,8 +70,8 @@
## Test Plan
- Add unit tests for `rpc/serverState.ts`: snapshot bootstrapping, stream replay, provider/settings updates.
- Add unit tests for git/project/checkpoint hooks: loading, error mapping, retry behavior, invalidation, keep-previous-result behavior.
-- Update the browser harness in [wsRpcHarness.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/test/wsRpcHarness.ts) to assert direct RPC + atom behavior instead of `__resetNativeApiForTests`.
-- Replace [wsNativeApi.test.ts](/Users/julius/.t3/worktrees/codething-mvp/effect-http-router/apps/web/src/wsNativeApi.test.ts), `gitReactQuery.test.ts`, `providerReactQuery.test.ts`, and `desktopUpdateReactQuery.test.ts` with equivalent atom-backed coverage.
+- Update the browser harness in [wsRpcHarness.ts](apps/web/test/wsRpcHarness.ts) to assert direct RPC + atom behavior instead of `__resetNativeApiForTests`.
+- Replace [wsNativeApi.test.ts](apps/web/src/wsNativeApi.test.ts), `gitReactQuery.test.ts`, `providerReactQuery.test.ts`, and `desktopUpdateReactQuery.test.ts` with equivalent atom-backed coverage.
- Acceptance scenarios:
- welcome still bootstraps snapshot and navigation
- keybindings toast still responds to config stream updates
diff --git a/apps/server/src/git/Layers/GitCore.ts b/apps/server/src/git/Layers/GitCore.ts
--- a/apps/server/src/git/Layers/GitCore.ts
+++ b/apps/server/src/git/Layers/GitCore.ts
@@ -1943,7 +1943,7 @@
// Refresh upstream refs in the background so checkout remains responsive.
yield* refreshCheckedOutBranchUpstream(input.cwd).pipe(
Effect.ignoreCause({ log: true }),
- Effect.forkDetach({ startImmediately: true }),
+ Effect.forkScoped,
);
},
);You can send follow-ups to this agent here.
Co-authored-by: codex <codex@users.noreply.github.com>
- Use `wss` when the page is served over HTTPS - Make transport/client disposal async so client scope closes before runtime teardown - Update tests for secure-origin URL selection and disposal order
- Assert client scope closes before runtime disposal - Use a mocked transport instance to keep ordering deterministic



What Changed
Why
UI Changes
Checklist
Note
High Risk
Large refactor of server boot/routing and WebSocket RPC surfaces plus packaging entrypoints; regressions could prevent the server or desktop app from starting or serving assets/RPC correctly.
Overview
Server startup is refactored to a new Effect CLI (
src/bin.ts+src/cli.ts) that resolves config from flags/env/bootstrap FD, ensures runtime directories exist, and launches a new Layer-composed server (src/server.ts) instead of the previousmain.ts/index.tswiring.The server now exposes a unified router (
src/http.ts) for static/dev redirect, attachments file serving, and project favicon requests, and adds/expands a WebSocket RPC route (/ws) with new seam tests (server.test.ts); lifecycle event streaming is formalized via a newServerLifecycleEventsservice.Cross-cutting cleanup moves several domain error types (git/text-generation/keybindings/open/server-settings) to
@t3tools/contracts, removes bespoke logger code, updates GitCore background refresh to detached fibers, and adjusts desktop + server packaging to usedist/bin.mjs(including dev-electron watcher updates) while increasing various integration test timeouts and adding requiredWorkspacePathswiring.Written by Cursor Bugbot for commit 0bb9550. This will update automatically on new commits. Configure here.
Note
Replace WebSocket push/subscribe transport with Effect RPC client and atom-backed server state
WsTransportpush/channel model with an Effect-based RPC protocol client (WsRpcGroup) for both the web client and server, using JSON serialization and automatic transient-error retry.serverState.ts) via@effect/atom-reactfor server config, keybindings, providers, and lifecycle events, replacing React Query throughoutChatView,Sidebar,SettingsPanels, and related components.@t3tools/contracts.GitCommandError,TextGenerationError,KeybindingsConfigError,ServerSettingsError, etc.) from local definitions into the@t3tools/contractspackage and removesgit/Errors.ts.ServerRuntimeStartupwith aCommandGatethat queues RPC commands until the server is ready, and addsServerLifecycleEventsfor publishing and snapshotting welcome/ready lifecycle events.index.tstobin.ts, adds@effect/platform-bunfor platform-specific HTTP/WebSocket serving, and removesws/@types/wsdependencies.NativeApino longer exposesrunStackedActionoronActionProgressdirectly; git action progress is now wired through the mutation'sonProgresscallback and the RPC client stream.Macroscope summarized 0bb9550.